/**
 * 自己简单实现一下 react-redux
 */

import {
  FC,
  ReactNode,
  createContext,
  useContext,
  useEffect,
  useMemo,
  useState,
} from "react";

export type ProviderProps = {
  store: any;
  children: ReactNode;
};

/**
 * 创建一个 StoreContext
 */
const StoreContext = createContext(null);

export const Provider: FC<ProviderProps> = ({ store, children }) => {
  // 将我收到的 store 通 context 往后代传递
  return (
    <StoreContext.Provider value={store}>{children}</StoreContext.Provider>
  );
};

export const connect = (mapStateToProps?, mapDispatchToProps?) => {
  // 返回一个高阶组件函数
  return (WrappedComponent) => {
    // 返回一个新组件
    return (props) => {
      // 用于存放 store 的 state 的数据
      const [state, setState] = useState({});

      // 获取 StoreContext 的 value 值，而它就是 store
      const store = useContext(StoreContext);

      // 基于现有的 state 和一些内容计算得出 newProps
      const newProps = useMemo(() => {
        let stateToProps = {};
        let dispatchToProps = {};
        // 如果 mapStateToProps 有传递的话
        if (mapStateToProps) {
          // 调用它获取它的返回值
          stateToProps = mapStateToProps(state);
        }

        // 如果 mapDispatchToProps 有传递的话
        if (mapDispatchToProps) {
          // 1. 将 mapDispatchToProps 这个对象中每个函数做改写，让其调用之后的返回值能自动触发 dispatch
          for (let key in mapDispatchToProps) {
            dispatchToProps[key] = function () {
              // 调用原本的函数,得到动作对象
              const action = mapDispatchToProps[key]();
              // 派发该动作
              store.dispatch(action);
            };
          }
        } else {
          dispatchToProps = {
            dispatch: store.dispatch,
          };
        }

        // 组装好 newProps
        return { ...props, ...stateToProps, ...dispatchToProps };
      }, [state, props]);

      useEffect(() => {
        // 默认将 store.getState() 赋值给 state
        setState(store.getState());

        store.subscribe(() => {
          setState(store.getState());
        });
      }, []);

      // 返回一个 JSX
      return <WrappedComponent {...newProps} />;
    };
  };
};
